/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "fxVSIncludes.fx"

struct SHADOWLIGHT_PS
{
	float4 Position: SV_POSITION;
	float2 TexCoord: TEXCOORD;
	float3 LightVector: LIGHTVECTOR;
	float3 ShadowCoord0: SHADOWCOORD0;
	float3 ShadowCoord1: SHADOWCOORD1;
	float3 ShadowCoord2: SHADOWCOORD2;
	float3 ShadowCoord3: SHADOWCOORD3;
	float3 Normal: NORMAL;
};

// split off because it's shared with the geometry shader
SHADOWLIGHT_PS CommonShadowLightVS (float4 Position, float2 TexCoord, float3 Normal)
{
	SHADOWLIGHT_PS vs_out;

	vs_out.Position = mul (EntityMatrix, Position);
	vs_out.TexCoord = TexCoord;
	vs_out.LightVector = TransformedLight - Position.xyz;

	// i need to flip it in z because right now i'm drawing my cubemap upside-down... :(
	vs_out.ShadowCoord0 = float3 (1, 1, -1) * -vs_out.LightVector;
	vs_out.ShadowCoord1 = float3 (1, 1, -1) * (float3 ( 1.0f,  2.0f, -1.0f) - vs_out.LightVector);
	vs_out.ShadowCoord2 = float3 (1, 1, -1) * (float3 ( 2.0f, -1.0f,  1.0f) - vs_out.LightVector);
	vs_out.ShadowCoord3 = float3 (1, 1, -1) * (float3 (-1.0f,  1.0f,  2.0f) - vs_out.LightVector);

	vs_out.Normal = mul ((float3x3) LocalMatrix, Normal);

	return vs_out;
}

struct SHADOW_GS
{
	float4 Position : POSITION;
};


struct SHADOW_PS
{
	float4 Position : SV_POSITION;
	float3 LightVector: LIGHTVECTOR;
	uint RTIndex : SV_RenderTargetArrayIndex;
	uint VPIndex : SV_ViewportArrayIndex;
};


#ifdef VERTEXSHADER
SURF_VS SurfShadowLightVS (SURF_VS vs_in)
{
	return vs_in;
}

SHADOWLIGHT_PS MeshShadowLightVS (MESH_VS vs_in)
{
	return CommonShadowLightVS (
		lerp (vs_in.Position0, vs_in.Position1, lerpBlend),
		vs_in.TexCoord,
		lerp (vs_in.Normal0, vs_in.Normal1, lerpBlend)
	);
}
#endif


#ifdef GEOMETRYSHADER
[maxvertexcount(3)]
void SurfShadowLightGS (triangle SURF_VS vs_in[3], inout TriangleStream<SHADOWLIGHT_PS> triStream)
{
	// duplicate Quake's fucked-up normals (QBSP didn't normalize this)
	float3 normal = cross (vs_in[0].Position.xyz - vs_in[1].Position.xyz, vs_in[2].Position.xyz - vs_in[1].Position.xyz);

	// and output our triangle
	for (int i = 0; i < 3; i++)
		triStream.Append (CommonShadowLightVS (vs_in[i].Position, vs_in[i].TexCoord, normal));

	// and finish it
	triStream.RestartStrip ();
}
#endif


/*
==============================================================================================================================

SHADOW MAP GENERATION

==============================================================================================================================
*/
#ifdef VERTEXSHADER
SHADOW_GS SurfShadowVS (SURF_VS vs_in)
{
	SHADOW_GS vs_out;

	vs_out.Position = mul (LocalMatrix, vs_in.Position);

	return vs_out;
}


SHADOW_GS MeshShadowVS (MESH_VS vs_in)
{
	SHADOW_GS vs_out;

	vs_out.Position = mul (LocalMatrix, lerp (vs_in.Position0, vs_in.Position1, lerpBlend));

	return vs_out;
}
#endif


#ifdef GEOMETRYSHADER
[maxvertexcount(18)]
void ObjectShadowGS (triangle SHADOW_GS input[3], inout TriangleStream<SHADOW_PS> CubeMapStream)
{
	for (int f = 0; f < 6; f++)
	{
		// this is really just 0 for not culled and 1 for culled
		if (ShadowCullingArray[f] < 0.5f)
		{
			// Compute screen coordinates
			SHADOW_PS output;

			output.RTIndex = f;
			output.VPIndex = 1;

			for (int v = 0; v < 3; v++)
			{
				output.Position = mul (ShadowMatrixArray[f], input[v].Position);
				output.LightVector = LightOrigin - input[v].Position.xyz;
				CubeMapStream.Append (output);
			}

			CubeMapStream.RestartStrip ();
		}
	}
}
#endif

#ifdef PIXELSHADER
/*
float4 ObjectShadowPS (SHADOW_PS ps_in) : SV_TARGET0
{
	float Depth = dot (ps_in.LightVector, ps_in.LightVector);
	return float4 (Depth, Depth, Depth, Depth);
}
*/
float4 ObjectShadowPS (SHADOW_PS ps_in) : SV_TARGET0
{
	float3 ScaledVector = (ps_in.LightVector / LightRadius);
	return dot (ScaledVector, ScaledVector);
}
#endif

#ifdef PIXELSHADER
TextureCube ShadowCube : register(t14);

float PSShadowCommon (SHADOWLIGHT_PS ps_in)
{
	float ShadowSample0 = ShadowCube.SampleLevel (lmapSampler, ps_in.ShadowCoord0, 0).x;
	float ShadowSample1 = ShadowCube.SampleLevel (lmapSampler, ps_in.ShadowCoord1, 0).x;
	float ShadowSample2 = ShadowCube.SampleLevel (lmapSampler, ps_in.ShadowCoord2, 0).x;
	float ShadowSample3 = ShadowCube.SampleLevel (lmapSampler, ps_in.ShadowCoord3, 0).x;

	float3 ScaledVector = (ps_in.LightVector / LightRadius) * DepthBias.x;
	float Depth = dot (ScaledVector, ScaledVector);
	float Shadow = 0.0f;

	Shadow += step (Depth, ShadowSample0) * 0.25f;
	Shadow += step (Depth, ShadowSample1) * 0.25f;
	Shadow += step (Depth, ShadowSample2) * 0.25f;
	Shadow += step (Depth, ShadowSample3) * 0.25f;

	return Shadow;
}

float4 SurfShadowLightPS (SHADOWLIGHT_PS ps_in) : SV_TARGET0
{
	float4 diff = surfTexture.Sample (surfSampler, ps_in.TexCoord);
	float Angle = ((dot (normalize (ps_in.Normal), normalize (ps_in.LightVector)) * 0.5f) + 0.5f) / 256.0f;
	float Add = max ((LightRadius - length (ps_in.LightVector)) * Angle, 0.0f);

	Add *= PSShadowCommon (ps_in);

	return float4 (diff.rgb * LightColor * Add, 0.0f);
}

float4 MeshShadowLightPS (SHADOWLIGHT_PS ps_in) : SV_TARGET0
{
	float4 diff = surfTexture.Sample (surfSampler, ps_in.TexCoord);
	float Angle = dot (normalize (ps_in.Normal), normalize (ps_in.LightVector));
	float Add = max ((LightRadius - length (ps_in.LightVector)) * max (Angle + 1.0f, (Angle * 0.2954545f) + 1.0f) / 512.0f, 0.0f);

	Add *= PSShadowCommon (ps_in);

	//return float4 (LightColor * Add, 0.0f);
	return float4 (diff.rgb * LightColor * Add, 0.0f);
}
#endif

